﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Guard.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Guards against common invalid argument conditions.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon
{
    using System;
    using System.Globalization;

    /// <summary>
    /// Guards against common invalid argument conditions.
    /// </summary>   
    /// <remarks>
    /// Argument exceptions always return the original argument value to allow chaining. 
    /// <example>
    /// <code>
    ///   public class BaseObject
    ///   {
    ///      public BaseObject(object value)
    ///      {
    ///         this.value = value;
    ///      }
    ///   }
    ///   <para />
    ///   // --------------------------------------------------------------
    ///   // Define a descendent class that requires value to be a non-null 
    ///   // reference using a guard condition in the call to base. 
    ///   // --------------------------------------------------------------
    ///   public class DescendentObject : BaseObject
    ///   {
    ///      public DescendentObject(object value)
    ///         : base(Guard.ArgumentNull("value", value))
    ///      {
    ///      }
    ///   } 
    /// </code>
    /// </example>
    /// </remarks>
    public static class Guard
    {
        #region Null Guards

        public static TArg? ArgumentNull<TArg>(string argumentName, TArg? argumentValue) where TArg : struct
        {
            if (!argumentValue.HasValue)
            {
                throw new ArgumentNullException(argumentName);
            }

            return argumentValue;
        }

        public static TArg ArgumentNull<TArg>(string argumentName, TArg argumentValue) where TArg : class
        {
            if (ReferenceEquals(argumentValue, null))
            {
                throw new ArgumentNullException(argumentName);
            }

            return argumentValue;
        }

        #endregion

        #region Type Guards

        public static TArg ArgumentWrongType<TArg>(string argumentName, object argumentValue)
        {
            if (!(argumentValue is TArg) && (argumentValue != null || typeof(TArg).IsValueType))
            {
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
                    ExceptionMessages.ArgumentInvalidType, typeof(TArg).FullName), argumentName);
            }
            return (TArg)argumentValue;
        }

        public static TArg ArgumentNullOrWrongType<TArg>(string argumentName, object argumentValue)
        {
            return ArgumentWrongType<TArg>(argumentName, ArgumentNull(argumentName, argumentValue));
        }

        public static void ArgumentNotAssignableAs(string argumentName, Type argumentValue, Type targetType)
        {
            if (!targetType.IsAssignableFrom(argumentValue))
            {
                throw new ArgumentException(
                   string.Format(
                      CultureInfo.CurrentCulture,
                      ExceptionMessages.ArgumentTypeIsAssignableFrom, targetType.FullName), argumentName);
            }
        }

        #endregion

        #region String Guards

        public static string ArgumentNullOrEmpty(string argumentName, string argumentValue)
        {
            ArgumentNull(argumentName, argumentValue);
            if (argumentValue.Length == 0)
            {
                throw new ArgumentException(ExceptionMessages.ArgumentEmpty, argumentName);
            }
            return argumentValue;
        }

        public static string ArgumentContains(string argumentName, string argumentValue, char[] illegalCharacters)
        {
            if (argumentValue != null && argumentValue.IndexOfAny(illegalCharacters) != -1)
            {
                throw new ArgumentException(ExceptionMessages.ArgumentIllegalCharacters, argumentName);
            }

            return argumentValue;
        }

        public static string ArgumentNullOrContains(string argumentName, string argumentValue, char[] illegalCharacters)
        {
            return ArgumentContains(argumentName, ArgumentNull(argumentName, argumentValue), illegalCharacters);
        }

        #endregion

        #region Range Guards

        public static int ArgumentLessThan(string argumentName, int argumentValue, int minimumValue)
        {
            if (argumentValue < minimumValue)
            {
                throw CreateArgumentOutOfRangeException(argumentName, argumentValue,
                        string.Format(CultureInfo.CurrentCulture, ExceptionMessages.ArgumentLessThan, minimumValue));
            }
            return argumentValue;
        }

        public static int ArgumentGreaterThan(string argumentName, int argumentValue, int maximumValue)
        {
            if (argumentValue > maximumValue)
            {
                throw CreateArgumentOutOfRangeException(argumentName, argumentValue,
                        string.Format(CultureInfo.CurrentCulture, ExceptionMessages.ArgumentGreaterThan, maximumValue));
            }
            return argumentValue;
        }

        public static int ArgumentOutOfRange(string argumentName, int argumentValue, int minimumValue, int maximumValue)
        {
            return ArgumentGreaterThan(argumentName, ArgumentLessThan(argumentName, argumentValue, minimumValue), 
                maximumValue);
        }

        public static long ArgumentLessThan(string argumentName, long argumentValue, long minimumValue)
        {
            if (argumentValue < minimumValue)
            {
                throw CreateArgumentOutOfRangeException(argumentName, argumentValue,
                   string.Format(CultureInfo.CurrentCulture, ExceptionMessages.ArgumentLessThan, minimumValue));
            }
            return argumentValue;
        }

        public static long ArgumentGreaterThan(string argumentName, long argumentValue, long maximumValue)
        {
            if (argumentValue > maximumValue)
            {
                throw CreateArgumentOutOfRangeException(
                   argumentName, argumentValue,
                   string.Format(CultureInfo.CurrentCulture, ExceptionMessages.ArgumentGreaterThan, maximumValue));
            }
            return argumentValue;
        }

        public static long ArgumentOutOfRange(string argumentName, long argumentValue, long minimumValue, long maximumValue)
        {
            return ArgumentGreaterThan(argumentName, ArgumentLessThan(argumentName, argumentValue, minimumValue), 
                maximumValue);
        }

        public static double ArgumentLessThan(string argumentName, double argumentValue, double minimumValue)
        {
            if (argumentValue < minimumValue)
            {
                throw CreateArgumentOutOfRangeException(
                   argumentName, argumentValue,
                   string.Format(CultureInfo.CurrentCulture, ExceptionMessages.ArgumentLessThan, minimumValue));
            }
            return argumentValue;
        }

        public static double ArgumentGreaterThan(string argumentName, double argumentValue, double maximumValue)
        {
            if (argumentValue > maximumValue)
            {
                throw CreateArgumentOutOfRangeException(
                   argumentName, argumentValue,
                   string.Format(CultureInfo.CurrentCulture, ExceptionMessages.ArgumentGreaterThan, maximumValue));
            }
            return argumentValue;
        }

        public static double ArgumentOutOfRange(string argumentName, double argumentValue, double minimumValue,
            double maximumValue)
        {
            return ArgumentGreaterThan(argumentName, ArgumentLessThan(argumentName, argumentValue, minimumValue), 
                maximumValue);
        }

        private static ArgumentOutOfRangeException CreateArgumentOutOfRangeException(
            string argumentName, object actualValue, string message)
        {
#if !SILVERLIGHT
            return new ArgumentOutOfRangeException(argumentName, actualValue, message);
#else
            return new ArgumentOutOfRangeException(argumentName, message);
#endif
        }

        #endregion
    }
}